package com.atguigu.gulimall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.gulimall.product.dao.CategoryDao;
import com.atguigu.gulimall.product.entity.CategoryEntity;
import com.atguigu.gulimall.product.service.CategoryBrandRelationService;
import com.atguigu.gulimall.product.service.CategoryService;
import com.atguigu.gulimall.product.vo.Catelog2Vo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.xdevapi.JsonString;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.stream.Collectors;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

//    @Autowired
//    CategoryDao categoryDao;

	@Autowired
	CategoryBrandRelationService categoryBrandRelationService;

	@Autowired
	StringRedisTemplate redisTemplate;

	@Autowired
	RedissonClient redissonClient;

	@Override
	public PageUtils queryPage(Map<String, Object> params) {
		IPage<CategoryEntity> page = this.page(new Query<CategoryEntity>().getPage(params),
				new QueryWrapper<CategoryEntity>());

		return new PageUtils(page);
	}

	@Override
	public List<CategoryEntity> listWithTree() {
		//1、查出所有分类
		List<CategoryEntity> entities = baseMapper.selectList(null);

		//2、组装成父子的树形结构

		//2.1）、找到所有的一级分类
		List<CategoryEntity> level1Menus =
				entities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0).map((menu) -> {
					menu.setChildren(getChildrens(menu, entities));
					return menu;
				}).sorted((menu1, menu2) -> {
					return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 :
							menu2.getSort());
				}).collect(Collectors.toList());


		return level1Menus;
	}

	@Override
	public void removeMenuByIds(List<Long> asList) {
		//TODO  1、检查当前删除的菜单，是否被别的地方引用

		//逻辑删除
		baseMapper.deleteBatchIds(asList);
	}

	//[2,25,225]
	@Override
	public Long[] findCatelogPath(Long catelogId) {
		List<Long> paths = new ArrayList<>();
		List<Long> parentPath = findParentPath(catelogId, paths);

		Collections.reverse(parentPath);


		return parentPath.toArray(new Long[parentPath.size()]);
	}

	/**
	 * 级联更新所有关联的数据
	 *
	 * @param category
	 */
	//@Caching(evict = {
	//		@CacheEvict(value = {"category"}, key = "'getCatelogPath'"),
	//		@CacheEvict(value = {"category"}, key = "'getCatlogJson'")
	//})
	@CacheEvict(value = {"category"}, allEntries = true) //第二种方法 推荐的
	@Transactional
	@Override
	public void updateCascade(CategoryEntity category) {
		//创建读写锁
		RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("catalogJson-lock");
		//创建写锁
		RLock lock = readWriteLock.writeLock();
		try {
			lock.lock();
			this.updateById(category);
			categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		//同时修改缓存中的数据
		//删除缓存,等待下一次主动查询进行更新

	}

	@Cacheable(value = {"category"}, key = "#root.methodName",sync = true)
	public List<CategoryEntity> getLevel1Categorys() {
		System.out.println("一级列表缓存");

		return this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq(
				"parent_cid", 0));
	}

	@Override
	@Cacheable(value = {"category"}, key = "#root.methodName")
	public Map<String, List<Catelog2Vo>> getCatlogJson() {
		return getCatlogJsonFromDb();
	}

	private Map<String, List<Catelog2Vo>> getCatlogJsonFromDb() {

		System.out.println("缓存不命中，将要查询数据库");
		//查询出所有数据
		List<CategoryEntity> selectList = this.baseMapper.selectList(null);
		//1.获取所有一级分类
		List<CategoryEntity> categoryEntities = getParent_cid(selectList, 0L);


		Map<String, List<Catelog2Vo>> collect =
				categoryEntities.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), l1 -> {
					//封装数据
					//获取到当前一级分类下的所有二级分类
					List<CategoryEntity> l2Entities = getParent_cid(selectList, l1.getCatId());

					//封装二级分类数据
					List<Catelog2Vo> catelog2Vos = null;
					if (l2Entities != null) {

						catelog2Vos = l2Entities.stream().map(l2 -> {
							Catelog2Vo catelog2Vo = new Catelog2Vo(l1.getCatId().toString(), null,
									l2.getCatId().toString(),
									l2.getName().toString());

							//获取当前二级分类下的所有三级分类，封装为VO
							List<CategoryEntity> l3Entities = getParent_cid(selectList, l2.getCatId());
							if (l3Entities != null) {
								//封装三级分类数据
								List<Catelog2Vo.Category3Vo> category3Vos = l3Entities.stream().map(l3 -> {
									//封装三级分类格式
									Catelog2Vo.Category3Vo category3Vo =
											new Catelog2Vo.Category3Vo(l2.getCatId().toString(),
													l3.getCatId().toString(), l3.getName());

									return category3Vo;
								}).collect(Collectors.toList());
								//封装二级分类
								catelog2Vo.setCatalog3List(category3Vos);
							}
							return catelog2Vo;
						}).collect(Collectors.toList());

					}

					//封装一级分类
					return catelog2Vos;
				}));
		return collect;
	}

	public Map<String, List<Catelog2Vo>> getCatlogJson2() {
		//	从Reids缓存中获取数据 获取需要反序列化为对象 缓存需要缓存为JSON
		String catalogJson = redisTemplate.opsForValue().get("catalogJson");
		if (StringUtils.isEmpty(catalogJson)) {
			//	业务获取数据
			return getCatlogJsonFromDbWithRedisLock();
		}
		System.out.println("缓存命中，直接返回");
		//返回Redis缓存中的数据
		return JSON.parseObject(catalogJson, new TypeReference<Map<String,
				List<Catelog2Vo>>>() {
		});

	}

	public Map<String, List<Catelog2Vo>> getCatlogJsonFromDbWithRedisLock() {

		//	设置分布式锁
		String uuid = UUID.randomUUID().toString();
		//设置过期时间防止死锁，设置uuid，防止删除别人的锁
		Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
		if (lock) {//如果获取锁就可以执行业务
			Map<String, List<Catelog2Vo>> dataFromDb = null;
			try {
				dataFromDb = getCatlogJsonFromDbByLock();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) " +
						"else" + " " + "return 0 end";
				//	删除锁
				redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
			}
			return dataFromDb;
		} else {
			System.out.println("获取分布式锁失败...等待重试...");
			//	加锁失败 重试机制  眠100s
			try {
				TimeUnit.MILLISECONDS.sleep(100);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return getCatlogJsonFromDbWithRedisLock();     //自旋的方式

		}

	}


	public Map<String, List<Catelog2Vo>> getCatlogJsonFromDbByLock() {

		return getCatlogJsonFromDb();
	}


	public synchronized Map<String, List<Catelog2Vo>> getCatlogJsonFromDbWithSynchronized() {

		//得到锁以后，我们应该再去缓存中确定一次，如果没有才需要继续查询
		//再次确认缓存中是否有缓存的数据，有就不用查了
		return getCatlogJsonFromDb();
	}


	private List<CategoryEntity> getParent_cid(List<CategoryEntity> selectList, Long parentId) {
		List<CategoryEntity> collect =
				selectList.stream().filter(categoryEntity -> Objects.equals(categoryEntity.getParentCid(), parentId)).collect(Collectors.toList());

		//List<CategoryEntity> entityList = this.baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq
		// ("parent_cid"
		//		, parentId));
		return collect;
	}

	//225,25,2
	private List<Long> findParentPath(Long catelogId, List<Long> paths) {
		//1、收集当前节点id
		paths.add(catelogId);
		CategoryEntity byId = this.getById(catelogId);
		if (byId.getParentCid() != 0) {
			findParentPath(byId.getParentCid(), paths);
		}
		return paths;

	}


	//递归查找所有菜单的子菜单
	private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {

		List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
			return categoryEntity.getParentCid() == root.getCatId();
		}).map(categoryEntity -> {
			//1、找到子菜单
			categoryEntity.setChildren(getChildrens(categoryEntity, all));
			return categoryEntity;
		}).sorted((menu1, menu2) -> {
			//2、菜单的排序
			return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
		}).collect(Collectors.toList());

		return children;
	}


}